#ifndef PUDGE_MATH_VEC_HPP
#define PUDGE_MATH_VEC_HPP

#include <cmath>

namespace pudge { namespace math { 

	template<int Size = 3, class Type = float> struct vec {};

	template<class Type> struct vec<2,Type>
	{
		const static int SIZE = 2;

		typedef Type value_type;

		union
		{	
			struct
			{
				value_type x, y;
			};
			struct
			{
				value_type s, t;
			};
		};
		value_type & operator [] (const unsigned int i)
		{
			return (&x)[i];
		}
		value_type & operator [] (const unsigned int i) const
		{
			return const_cast<int&>((&x)[i]);
		}
		vec(const value_type a, const value_type b)
			: x(a), y(b)
		{}
		vec() : x((value_type)0), y((value_type)0)
		{}
		vec<SIZE,value_type>& operator()(const value_type a, const value_type b)
		{
			x = a;
			y = b;
		}
	};

	template<class Type> struct vec<3,Type>
	{
		const static int SIZE = 3;

		typedef Type value_type;

		union
		{	
			struct
			{
				value_type x, y, z;
			};
			struct
			{
				value_type s, t, u;
			};
			struct
			{
				value_type r, g, b;
			};
		};
		value_type& operator [] (const unsigned int i)
		{
			return (&x)[i];
		}
		value_type& operator [] (const unsigned int i) const
		{
			return const_cast<value_type&>((&x)[i]);
		}
		vec(const value_type a, const value_type b, const value_type c)
			: x(a), y(b), z(c)
		{}
		vec() : x((value_type)0), y((value_type)0), z((value_type)0)
		{}
		vec<SIZE,value_type>& operator()(const value_type a, const value_type b, const value_type c)
		{
			x = a;
			y = b;
			z = c;
			return *this;
		}
	};

	template<class Type> struct vec<4,Type>
	{
	public:

		const static int SIZE = 4;

		typedef Type value_type;

		union
		{	
			struct
			{
				value_type x, y, z, w;
			};
			struct
			{
				value_type s, t, u, v;
			};
			struct
			{
				value_type r, g, b, a;
			};
		};

		value_type& operator [] (const unsigned int i)
		{
			return (&x)[i];
		}
		value_type& operator [] (const unsigned int i) const
		{
			return const_cast<int&>((&x)[i]);
		}
		vec(const value_type a, const value_type b, const value_type c, const value_type d)
			: x(a), y(b), z(c), w(d)
		{}
		vec() : x((value_type)0), y((value_type)0), z((value_type)0), w((value_type)0)
		{}
		vec<SIZE,value_type>& operator()(
			const value_type a, const value_type b, const value_type c, const value_type d)
		{
			x = a;
			y = b;
			z = c;
			w = d;
			return *this;
		}
	};

	// vector addition
	template<class Type>
	inline vec<4,Type> & add(vec<4,Type> & d, vec<4,Type> const& a, vec<4,Type> const& b) 
	{
		d.x = a.x + b.x;
		d.y = a.y + b.y;
		d.z = a.z + b.z;
		d.w = a.w;
		return d;
	}
	template<class Type>
	inline vec<3,Type> & add(vec<3,Type> & d, vec<3,Type> const& a, vec<3,Type> const& b) 
	{
		d.x = a.x + b.x;
		d.y = a.y + b.y;
		d.z = a.z + b.z;
		return d;
	}
	template<class Type>
	inline vec<2,Type> & add(vec<2,Type> & d, vec<2,Type> const& a, vec<2,Type> const& b) 
	{
		d.x = a.x + b.x;
		d.y = a.y + b.y;
		return d;
	}
	template<int Size, class Type>
	inline vec<Size,Type> & operator += (vec<Size,Type> & a, vec<Size,Type> const& b)
	{
		return add(a,a,b);
	}

	/// vector subtraction
	template<class Type>
	inline vec<4,Type> & sub(vec<4,Type> & d, vec<4,Type> const& a, vec<4,Type> const& b) 
	{
		d.x = a.x - b.x;
		d.y = a.y - b.y;
		d.z = a.z - b.z;
		d.w = a.w;
		return d;
	}
	template<class Type>
	inline vec<3,Type> & sub(vec<3,Type>& d, vec<3,Type> const& a, vec<3,Type> const& b) 
	{
		d.x = a.x - b.x;
		d.y = a.y - b.y;
		d.z = a.z - b.z;
		return d;
	}
	template<class Type>
	inline vec<2,Type> & sub(vec<2,Type> & d, vec<2,Type> const& a, vec<2,Type> const& b) 
	{
		d.x = a.x - b.x;
		d.y = a.y - b.y;
		return d;
	}
	template<int Size, class Type>
	inline vec<Size,Type> & operator -= (vec<Size,Type> & a, vec<Size,Type> const& b)
	{
		return sub(a,a,b);
		return a;
	}

	/// vector multiplication by a scalar
	template<class Type>
	inline vec<4,Type> & mul(vec<4,Type> & d, vec<4,Type> const& a, const Type s) 
	{
		d.x = a.x * s;
		d.y = a.y * s
		d.z = a.z * s
		d.w = a.w;
		return d;
	}
	template<class Type>
	inline vec<3,Type> & mul(vec<3,Type>& d, vec<3,Type> const& a, const Type s) 
	{
		d.x = a.x * s;
		d.y = a.y * s
		d.z = a.z * s
		return d;
	}
	template<class Type>
	inline vec<2,Type> & mul(vec<2,Type> & d, vec<2,Type> const& a, const Type s) 
	{
		d.x = a.x * s;
		d.y = a.y * s
			return d;
	}
	template<int Size, class Type>
	inline vec<Size,Type> & operator *= (vec<Size,Type>& a, const Type b)
	{
		return mul(a,a,b);
	}

	/// vector division by a scalar
	template<class Type>
	inline vec<4,Type> & div(vec<4,Type>& d, vec<4,Type> const& a, const Type s) 
	{
		d.x = a.x / s;
		d.y = a.y / s
		d.z = a.z / s
		d.w = a.w;
		return d;
	}
	template<class Type>
	inline vec<3,Type> & div(vec<3,Type> & d, vec<3,Type> const& a, const Type s) 
	{
		d.x = a.x / s;
		d.y = a.y / s
		d.z = a.z / s
		return d;
	}
	template<class Type>
	inline vec<2,Type> & div(vec<2,Type>& d, vec<2,Type> const& a, const Type s) 
	{
		d.x = a.x / s;
		d.y = a.y / s;
		return d;
	}
	template<int Size, class Type>
	inline vec<Size,Type> & operator /= (vec<Size,Type>& a, const Type b)
	{
		return mul(a,a,b);
	}
	template<int Size, class Type>
	inline vec<Size,Type> & negate(vec<Size,Type> & v)
	{
		v.x = -v.x;
		v.y = -v.y;
		v.z = -v.z;
		return v;
	}

	/// dot product
	template<class Type>
	inline Type dot(vec<4,Type> const& a, vec<4,Type> const& b)
	{
		return a.x*b.x + a.y*b.y + a.z*b.z;
	}
	template<class Type>
	inline Type dot(vec<3,Type> const& a, vec<3,Type> const& b)
	{
		return a.x*b.x + a.y*b.y + a.z*b.z;
	}
	template<class Type>
	inline Type dot(vec<2,Type> const& a, vec<2,Type> const& b)
	{
		return a.x*b.x + a.y*b.y;
	}
	template<int Size, class Type>
	inline Type operator * (vec<Size,Type> const& a, vec<Size,Type> const& b)
	{
		return dot(a,b);
	}

	/// magnitude
	template<class Type>
	inline Type mag(vec<4,Type> const& v)
	{
		return sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
	}
	template<class Type>
	inline Type mag(vec<3,Type> const& v)
	{
		return sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
	}
	template<class Type>
	inline Type mag(vec<2,Type> const& v)
	{
		return sqrt(v.x*v.x + v.y*v.y);
	}

	/// normalize
	template<class Type>
	inline vec<4,Type> & normalize(vec<4,Type> & d, vec<4,Type> const& v)
	{
		const Type invMag = (Type)1.0 / mag(v);
		d.x = v.x * invMag;
		d.y = v.y * invMag;
		d.z = v.z * invMag;
		return d;
	}
	template<class Type>
	inline vec<3,Type> & normalize(vec<3,Type>& d, vec<3,Type> const& v)
	{
		const Type invMag = (Type)1.0 / mag(v);
		d.x = v.x * invMag;
		d.y = v.y * invMag;
		d.z = v.z * invMag;
		return d;
	}
	template<class Type>
	inline vec<2,Type> & normalize(vec<2,Type>& d, vec<2,Type> const& v)
	{
		const Type invMag = (Type)1.0 / mag(v);
		d.x = v.x * invMag;
		d.y = v.y * invMag;
		return d;
	}
	template<int Size, class Type>
	inline vec<Size,Type> & normalize(vec<Size,Type> & v)
	{
		return normalize(v,v);
	}

	/// cross product
	template<class Type>
	inline vec<4,Type> & cross(vec<4,Type> & d, vec<4,Type> const& a, vec<4,Type> const& b)
	{
#ifdef _DEBUG
		if(&d == &a || &d == &b)
			throw -1;
#endif
		d.x = (a.y * b.z) - (a.z * b.y);
		d.y = (a.z * b.x) - (a.x * b.z);
		d.z = (a.x * b.y) - (a.y * b.x);

		return d;
	}
	template<class Type>
	inline vec<3,Type> & cross(vec<3,Type> & d, vec<3,Type> const& a, vec<3,Type> const& b)
	{
#ifdef _DEBUG
		if(&d == &a || &d == &b)
			throw -1;
#endif
		d.x = (a.y * b.z) - (a.z * b.y);
		d.y = (a.z * b.x) - (a.x * b.z);
		d.z = (a.x * b.y) - (a.y * b.x);

		return d;
	}
}}
#endif